#include <boost/array.hpp>
#include <boost/foreach.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>

#include <cassert>
#include <iostream>
#include <fstream>
#include <map>
#include <string>

#include "common.hpp"
#include "program.hpp"
#include "opcode.hpp"



Program::Program()
{
}
namespace {
	// this should probably be endian safe :D
	Instruction read_instruction(std::istream & stream) {
		assert(stream.good());

		const unsigned ints_in_instruction = 5;

		int v[ints_in_instruction];

		const int bytes_in_instruction = sizeof(int) * sizeof(v);

		stream.read(reinterpret_cast<char *>(&v), bytes_in_instruction);

		assert(stream.gcount() == bytes_in_instruction);

		return Instruction(v[0], v[1], v[2], v[3], v[4]);
	}
}


Program::Program(std::istream & stream)
{
	do {
		push_back(read_instruction(stream));

		// TODO: there should be a better way of doing this, lol..
		char fake; // read a char, then put it back -- so we know if we're at the end
		stream.read(&fake, 1);
		if (stream.gcount() == 1)
			stream.putback(fake);

	} while (!stream.eof());

}

Program::Program(Program const& other)
{
	for(OrderType::const_iterator it(other.instruction_order_.begin()) ; it != other.instruction_order_.end() ; ++it) {
		push_back(*it);
	}
}


void Program::push_back(Instruction const& inst)
{
	instruction_order_.push_back(
		*instructions_.insert(inst.id, ProgramInstruction(inst.id, inst.op, inst.argA, inst.argB, inst.ret))
	);

}

int Program::create_before(int before, int opcode)
{
	int new_id( instructions_.unused_key() );

	instruction_order_.insert( instruction_order_.s_iterator_to(instructions_.at(before)),
		*instructions_.insert(new_id, ProgramInstruction(new_id, opcode, 0, 0, 0))
	);

	return new_id;
}

int Program::create_after(int after, int opcode)
{
	int new_id( instructions_.unused_key() );

	OrderType::iterator place(instruction_order_.s_iterator_to(instructions_.at(after)));
	assert(place != instruction_order_.end());
	++place;

	instruction_order_.insert(place,
		*instructions_.insert(new_id, ProgramInstruction(new_id, opcode, 0, 0, 0))
	);

	return new_id;
}

void Program::set(ChangeType ct, int identifier, int value)
{
	assert(!instructions_.empty());
	switch (ct) {
	case CHANGE_OP:
		instructions_.at(identifier).op = value;
		return;
	case CHANGE_ARGA:
		instructions_.at(identifier).argA = value;
		return;
	case CHANGE_ARGB:
		instructions_.at(identifier).argB = value;
		return;
	case CHANGE_RET:
		instructions_.at(identifier).ret = value;
		return;
	}
	assert(false);
}

// before anyone bitches about this giant fuck-off function (and repeating myself like many times), I had properly factored out this into subfunctions and generating opcodes properly etc, but the additional complexity and speed cost is not nearly worth it. Although, if someone has a (working) patch that makaes all this /better/ i'll commit it ;D
Program::ExecutionResult Program::execute(const std::vector<IntegerArray> &input, unsigned max_cost)
{
	ExecutionResult res;

	assert(!instructions_.empty());

	OrderType::iterator inst = instruction_order_.begin();

	while (true)
	{
		//assert(inst);
		//assert(inst->next);

		if (++res.cost_ > max_cost) {
			res.error_ = Program::ExecutionResult::MaxExecution;
			return res;
		}
		//std::cout << "Running instruction: " << inst->id << std::endl;

		Mnemonic mnem(get_mnemonic(inst->op));
		ChangeType ct(get_changetype(inst->op));

		switch (mnem)
		{
		case NOOP:
			++inst;
			break;
		case ADD:
			set(ct, inst->ret, (inst->argA + inst->argB));
			++inst;
			break;
		case SUB:
			set(ct, inst->ret, (inst->argA - inst->argB));
			++inst;
			break;
		case MULT:
			set(ct, inst->ret, (inst->argA * inst->argB));
			++inst;
			break;
		case DIV:
			set(ct, inst->ret, (inst->argB ? inst->argA / inst->argB : 0));
			++inst;
			break;
		case MIN:
			set(ct, inst->ret, std::min(inst->argA, inst->argB));
			++inst;
			break;
		case MAX:
			set(ct, inst->ret, std::max(inst->argA, inst->argB));
			++inst;
			break;
		case MOD:
			set(ct, inst->ret, (inst->argB == 0) ? 0 : mod(inst->argA, inst->argB));
			++inst;
			break;
		case AND:
			set(ct, inst->ret, (inst->argA & inst->argB));
			++inst;
			break;
		case OR:
			set(ct, inst->ret, (inst->argA | inst->argB));
			++inst;
			break;
		case XOR:
			set(ct, inst->ret, (inst->argA ^ inst->argB));
			++inst;
			break;
		case LSHIFT:
			set(ct, inst->ret, (inst->argA << inst->argB));
			++inst;
			break;
		case RSHIFT:
			set(ct, inst->ret, (inst->argA >> inst->argB));
			++inst;
			break;
		case EQ:
			set(ct, inst->ret, (inst->argA == inst->argB ? 1 : 0));
			++inst;
			break;
		case NEQ:
			set(ct, inst->ret, (inst->argA != inst->argB ? 1 : 0));
			++inst;
			break;
		case LT:
			set(ct, inst->ret, (inst->argA < inst->argB ? 1 : 0));
			++inst;
			break;
		case LTE:
			set(ct, inst->ret, (inst->argA <= inst->argB ? 1 : 0));
			++inst;
			break;
		case GT:
			set(ct, inst->ret, (inst->argA > inst->argB ? 1 : 0));
			++inst;
			break;
		case GTE:
			set(ct, inst->ret, (inst->argA >= inst->argB ? 1 : 0));
			++inst;
			break;
		case IF:
			if (inst->argA) {
				inst = OrderType::s_iterator_to(instructions_.at(inst->argB));
			} else {
				++inst;
			}
			break;
		case GETI:
			set(ct, inst->ret, instructions_.at(inst->argA).id);
			++inst;
			break;
		case GETO:
			set(ct, inst->ret, instructions_.at(inst->argA).op);
		case GETA:
			set(ct, inst->ret, instructions_.at(inst->argA).argA);
			++inst;
			break;
		case GETB:
			set(ct, inst->ret, instructions_.at(inst->argA).argB);
			++inst;
			break;
		case GETR:
			set(ct, inst->ret, instructions_.at(inst->argA).ret);
			++inst;
			break;
		case AFTER:
			set(ct, inst->ret, (++OrderType::s_iterator_to(instructions_.at(inst->argA)))->id);
			++inst;
			break;
		case BEFORE:
			set(ct, inst->ret, (--OrderType::s_iterator_to(instructions_.at(inst->argA)))->id);
			++inst;
			break;
		case CREATE_BEFORE:
			set(ct, inst->ret, create_before(inst->argA, inst->argB));
			++inst;
			break;
		case CREATE_AFTER:
			set(ct, inst->ret, create_after(inst->argA, inst->argB));
			++inst;
			break;
		case REMOVE: {
			assert(!instructions_.empty());

			OrderType::const_iterator elem(OrderType::s_iterator_to(instructions_.at(inst->argA)));

			bool deleting_self = elem == inst;
			if (deleting_self)
				++inst; // avoid trashing our own iterator
			// and if we're not deleting outselves, we should wait till incrementing, incase we're deleting the next..

			instruction_order_.erase(elem);
			instructions_.erase(inst->argA);

			if (instructions_.empty()) {
				assert(deleting_self);
				// we just deleted ourselves, the last instruction, lol :(
				res.error_ = ExecutionResult::NoInstructions;
				return res;
			}

			if (!deleting_self)
				++inst;

			break;
		}
		case RAND: {
			// TODO: better control of random, along with making making sure it's got more bits of random
			set(ct, inst->ret, ::rand());
			++inst;

			break;
		}

		case READ: {
			const int collection = inst->argA;
			const int index = inst->argB;
			int read_val;

			if (collection != 0) {
				if (!input.empty()) {
					IntegerArray const& arr = input.at(mod(collection-1, input.size())); // collection is 1 based, native is 0 based
					if (index == 0) {
						read_val = arr.size();
					} else if (!arr.empty()) {
						read_val = arr[mod(index-1, arr.size())];
					} else {
						read_val = 0;
					}
				} else {
					read_val = 0;
				}
			} else {
				read_val = static_cast<int>(input.size());
				assert(read_val >= 0);
			}

			set(ct, inst->ret, read_val);
			++inst;
			break;
		}
		case EXIT:
			res.ret_code_ = inst->argA;
			return res;
		case NUMBER_OF_MNEMONICS: // avoid compiler warning
			assert(false);
		default:
			assert(false);
		}
	}


}

void Program::write(std::ostream & stream) const
{
	for (OrderType::const_iterator it(instruction_order_.begin()); it != instruction_order_.end(); ++it) {
		stream.write(reinterpret_cast<const char*>(it->id), sizeof(int));
		stream.write(reinterpret_cast<const char*>(it->op), sizeof(int));
		stream.write(reinterpret_cast<const char*>(it->argA), sizeof(int));
		stream.write(reinterpret_cast<const char*>(it->argB), sizeof(int));
		stream.write(reinterpret_cast<const char*>(it->ret), sizeof(int));
	}
}

namespace {
char get_ct_char(ChangeType ct) {
	switch (ct) {
	case CHANGE_OP:
		return 'o';
	case CHANGE_ARGA:
		return 'a';
	case CHANGE_ARGB:
		return 'b';
	case CHANGE_RET:
		return 'r';
	}

	assert(false);
}

}

std::string Program::disassemble() const
{
	std::string ret;
	ret += "# PRETTY PRINTING (instructions: " + boost::lexical_cast<std::string>(instructions_.size()) + ")\n";

	boost::array<unsigned, 5> maxLength;
	for (int i(0); i < 5 ; ++i) {
		maxLength[i] = 0;
	}

	std::list< boost::array<std::string, 6> > info;

	for (OrderType::const_iterator it(instruction_order_.begin()); it != instruction_order_.end(); ++it)
	{
		boost::array<std::string, 6> arr;
		arr[0] = boost::lexical_cast<std::string>(it->id);
		arr[1] = boost::lexical_cast<std::string>(it->op);
		arr[2] = boost::lexical_cast<std::string>(it->argA);
		arr[3] = boost::lexical_cast<std::string>(it->argB);
		arr[4] = boost::lexical_cast<std::string>(it->ret);
		arr[5] = "# " + show(get_mnemonic(it->op)) + get_ct_char(get_changetype(it->op)) + " ";

		for (int i(0); i < 5; ++i)
			if (maxLength[i] < arr[i].size())
				maxLength[i] = arr[i].size();

		info.push_back(arr);
	}

	for (std::list< boost::array<std::string, 6> >::iterator it(info.begin()) ; it != info.end() ; ++it)
	{
		for (int i(0); i < 6 ; ++i)
		{
			ret += (*it)[i];

			if (i != 5) { // don't white-space fill the last column
				for (unsigned counter = (*it)[i].size()-1; counter <= maxLength[i]; ++counter) {
					ret += ' ';
				}
			}

		}
		ret += '\n';
	}

	return ret;
}

Program::ProgramInstruction::ProgramInstruction(int id, int op, int argA, int argB, int ret)
	: boost::intrusive::list_base_hook<>()
	, Instruction(id, op, argA, argB, ret)
{
}

